package utils

import (
	"fmt"
	"math/rand"
	"reflect"
	"regexp"
	"strconv"
	"strings"
	"time"

	"github.com/beego/beego/v2/core/logs"
)

type argInt []int

// get int by index from int slice
func (a argInt) get(i int, args ...int) (r int) {
	if i >= 0 && i < len(a) {
		r = a[i]
	}
	if len(args) > 0 {
		r = args[0]
	}
	return
}

// ToStr interface to string
func ToStr(value interface{}, args ...int) (s string) {
	switch v := value.(type) {
	case bool:
		s = strconv.FormatBool(v)
	case float32:
		s = strconv.FormatFloat(float64(v), 'f', argInt(args).get(0, -1), argInt(args).get(1, 32))
	case float64:
		s = strconv.FormatFloat(v, 'f', argInt(args).get(0, -1), argInt(args).get(1, 64))
	case int:
		s = strconv.FormatInt(int64(v), argInt(args).get(0, 10))
	case int8:
		s = strconv.FormatInt(int64(v), argInt(args).get(0, 10))
	case int16:
		s = strconv.FormatInt(int64(v), argInt(args).get(0, 10))
	case int32:
		s = strconv.FormatInt(int64(v), argInt(args).get(0, 10))
	case int64:
		s = strconv.FormatInt(v, argInt(args).get(0, 10))
	case uint:
		s = strconv.FormatUint(uint64(v), argInt(args).get(0, 10))
	case uint8:
		s = strconv.FormatUint(uint64(v), argInt(args).get(0, 10))
	case uint16:
		s = strconv.FormatUint(uint64(v), argInt(args).get(0, 10))
	case uint32:
		s = strconv.FormatUint(uint64(v), argInt(args).get(0, 10))
	case uint64:
		s = strconv.FormatUint(v, argInt(args).get(0, 10))
	case string:
		s = v
	case []byte:
		s = string(v)
	default:
		s = fmt.Sprintf("%v", v)
	}
	return s
}

// ToUint32 string to uint32
func ToUint32(value interface{}) uint32 {
	v, err := strconv.ParseUint(ToStr(value), 10, 32)
	if err != nil {
		logs.Error(err)
		return 0
	}

	return uint32(v)
}

// ToInt64 interface to int64
func ToInt64(value interface{}) (d int64) {
	val := reflect.ValueOf(value)
	switch value.(type) {
	case int, int8, int16, int32, int64:
		d = val.Int()
	case uint, uint8, uint16, uint32, uint64:
		d = int64(val.Uint())
	default:
		panic(fmt.Errorf("ToInt64 need numeric not `%T`", value))
	}
	return
}

// CheckSQLInject 检查字符串是否存在sql注入
// 参考自: golang防止SQL注入攻击
// https://blog.csdn.net/qq_30505673/article/details/103335171
func CheckSQLInject(str string) bool {
	rule := `\b(insert|update|delete|drop|exec|execute)\b`
	re, err := regexp.Compile(rule)
	if err != nil {
		return false
	}
	return re.MatchString(strings.ToLower(str))
}

// RandStr 随机字符串
func RandStr(size int, kind int) string {
	ikind, kinds, result := kind, [][]int{{10, 48}, {26, 65}, {26, 97}}, make([]byte, size)
	isAll := kind < 0 || kind > 2
	rand.Seed(time.Now().UnixNano())
	for i := 0; i < size; i += 1 {
		if isAll {
			ikind = rand.Intn(3)
		}
		scope, base := kinds[ikind][0], kinds[ikind][1]
		result[i] = uint8(base + rand.Intn(scope))
	}
	return string(result)
}
